package com.ztrue.framework.core.domain.impl;

import com.ztrue.framework.core.domain.IAggregateRoot;
import com.ztrue.framework.core.domain.IAggregateStorage;
import com.ztrue.framework.core.event.IDomainEvent;
import com.ztrue.framework.core.event.IEventStore;
import com.ztrue.framework.exception.InvalidOperationException;
import com.ztrue.framework.infrastructure.IClassProvider;
import com.ztrue.common.log.Logger;

import java.util.List;

/**
 * 事件源聚合存储
 *
 * @author zhangw
 * @date 2016/11/2 16:32
 * @version: 1.0
 */
public class EventSourcingAggregateStorage implements IAggregateStorage {
    private IEventStore eventStore;
    private IClassProvider classProvider;
    private final static Logger logger = Logger.getLogger(IAggregateStorage.class);

    public EventSourcingAggregateStorage(IEventStore eventStore, IClassProvider classProvider) {
        this.eventStore = eventStore;
        this.classProvider = classProvider;
    }

    /**
     * 从聚合存储中获取一个聚合根
     *
     * @param aggregateRootId
     * @param aggregateRootType
     * @return
     */
    @Override
    public <T extends IAggregateRoot> T get(String aggregateRootId, Class<T> aggregateRootType) {
        try {
            List<IDomainEvent> domainEvents = eventStore.queryAggregateEvents(aggregateRootId, classProvider.getName(aggregateRootType));
            return rebuildAggregateRoot(aggregateRootType, domainEvents);
        } catch (Exception e) {
            String msg = String.format("领域对象：%s - [%s]，获取失败", aggregateRootType, aggregateRootId);
            logger.error(msg, e);
            throw new InvalidOperationException(msg, e);
        }
    }

    /**
     * 根据领域事件回溯领域对象
     *
     * @param aggregateRootType
     * @param domainEvents
     * @param <T>
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private <T extends IAggregateRoot> T rebuildAggregateRoot(Class<T> aggregateRootType, List<IDomainEvent> domainEvents) throws IllegalAccessException, InstantiationException {
        if (domainEvents == null || domainEvents.size() == 0) return null;
        T aggregateRoot = aggregateRootType.newInstance();
        aggregateRoot.replayEvents(domainEvents.iterator());
        return aggregateRoot;
    }
}
